#ifndef HTML_PARSER_H_INCLUDED
#define HTML_PARSER_H_INCLUDED

#include "html_document.h"
#include "html_attribute.h"
#include "html_tag.h"
#include <string.h>

class html_parser {
    public:

    html_parser() {} ;
    ~html_parser() {};

    html_document*  html_doc;

    // helper .. store all name's in lower case
    string make_lower(string s)
    {
        for(int i=0; i!=(int)s.length(); ++i)
            s[i] = std::tolower(s[i]);

        return s;

    }
    // split the args
    html_attrib *create_tag_args(html_tag* owner,const char *string)
    {
        char *p;
        char name[256] = {0};
        char value[256] = {0};
        html_attrib *first = 0;
        html_attrib *prior = 0;

        p = name;

        while(1) {

            /* Skip spaces */

            while(*string && isspace(*string))
                ++string;

            if(*string == '=') {

                /* Skip over '=' */
                ++string;

                *p = 0;
                p = value;


                if(*string == '"') {

                    ++string; /* skip open '"' */

                    while(*string && *string != '"')
                        *p++ = *string++;

                    ++string; /* skip 3 '"' */

                } else  {

                    while(*string && !isspace(*string))
                        *p++ = *string++;
                }

                *p = 0;

                html_attrib *result = new html_attrib();
                if(!result)return 0;

                if (!first) first = result;
                if (!owner->first_child) owner->first_child = result;
                p = name;

                result->name = make_lower(name);
                result->value = value;
                result->parent = owner;
                result->attrib_hash = create_hash((char*)make_lower(name).c_str());

                if (prior) prior->next = result;

                prior = result;


            } else if(isspace(*string) || *string == 0) {

                if(*string == 0) break;
                /* Attribute without a value (like 'selected' or 'checked') */

                *p = 0;

                html_attrib *result = new html_attrib();//add_node(result, name, "");
                if(!result) {
                    return 0;
                }
                if (!first) first = result;
                if (!owner->first_child) owner->first_child = result;

                result->name = make_lower(name);
                result->value = value;
                result->parent = owner;
                result->attrib_hash = create_hash((char*)make_lower(name).c_str());

                if (prior) prior->next = result;

                prior = result;



                ++string;

            } else
                *p++ = *string++;
        }

        return first;
    };

    void can_close_tag(unsigned long tag)
    {
        if (tag != hash_img && tag != hash_br && tag != hash_input && tag != hash_hr &&
            tag != hash_meta && tag != hash_link)
        return;

        html_doc->current_t = html_doc->current_t->parent_tag;
    };

    // process the attributes
    html_tag* process_opening_tag(char *tag,char* args)
    {
        html_tag* x = new html_tag();
        x->name = make_lower(tag); // just for reading / understanding.  Will remove later
        x->tag_hash = create_hash((char*)make_lower(tag).c_str());
        x->first_child = 0;

        x->parent_tag = html_doc->current_t; //save the parent tag

        if (args) x->first_child = create_tag_args(x,args);

        // save the address
        html_doc->html_t.push_back(x);
        html_doc->current_t = x;
        if (!html_doc->first_t) html_doc->first_t = x; // pointer to first tag in document

        can_close_tag(x->tag_hash); // check for single markup's like IMG

        return x;
    }



    // find the html TAG
    char *open_tag(const char *html)
    {
        char* tag = 0;
        char* rest = 0;
        const char *tmp;
        html_tag* ret = 0;

        tmp = html;

        while (*html && !isspace(*html) && *html != '>')
                ++html;

        tag = new char[(html - tmp + 1)];
        if(!tag) return 0;

        strncpy(tag, tmp, html - tmp);
        tag[html - tmp] = 0;

         // -- script? style?
        string t = tag;
        if (hash_script == create_hash((char*)make_lower(t).c_str()))
        {
            return parse_script((char*)html);
        }
        // -----

        if (*html == '>') {

            ret = process_opening_tag(tag,0); //no attributes/args/no chldren
            if(*html == '>')
                ++html;

            delete [] tag;
            return (char*)html;
        }

        /* Now extract the args */

        while (*html && isspace(*html))
            ++html;

        tmp = html;

        while (*html && *html != '>')
                ++html;

        rest = new char[(html - tmp + 1)];
        if(!rest) {
            delete [] tag;
            return 0;
        }

        strncpy(rest, tmp, html - tmp);
        rest[html - tmp] = 0;

        ret = process_opening_tag(tag,rest);

        if(*html == '>')
            ++html;

        delete [] tag;
        delete [] rest;

        return (char*)html;
    };

    char *close_tag(char *html)
    {
        char *tmp;
        char *tmp2;
        char* real_value;
        int ret = 0;

        ++html;

        tmp2 = html;
        while (*html && *html != '>')
            ++html;

        tmp = new char[(html - tmp2 + 2)];
        if (!tmp) return 0;

        tmp[0] = '_';
        strncpy(tmp + 1, tmp2, html - tmp2);
        tmp[html - tmp2 + 1] = 0;

        real_value = tmp+1; // pointer to the real text data

        if (ret) {
            delete [] tmp;
            return 0;
        }

        if (*html == '>')
            ++html;



        // build tree
        html_tag* t = html_doc->current_t;
        while(t)
        {

            if (t->tag_hash == create_hash((char*)make_lower(real_value).c_str())) {

                html_doc->current_t = html_doc->current_t->parent_tag;

                // save the end tag. Useful for render
                html_tag* x = new html_tag();
                x->name = (char*)make_lower(tmp).c_str();  // remove later
                x->tag_hash = create_hash((char*)make_lower(tmp).c_str());
                x->parent_tag = html_doc->current_t;
                html_doc->html_t.push_back(x);

                break;
            }

            html_doc->current_t = t;
            t = html_doc->current_t->parent_tag;
        }




        delete [] tmp;

        return html;
    };

    // --- not saving it right now, just removing from file!!!!!
    char* parse_script(char* html_text)
    {

        char *script;
        char *tmp;
        script = html_text;

        /* Locate </script> */

        while (*script) {
            if (*script == '<') {
                if (*(script + 2) == 's' || *(script + 2) == 'S') {
                    if (*(script + 7) == 't' || *(script + 7) == 'T') {
                        break;
                    }
                }
            }
            ++script;
        }

        tmp = new char[(script - html_text + 1)];
        if(!tmp) return 0;

        strncpy(tmp, html_text, script -html_text);
        tmp[script - html_text] = 0;

        // TO DO .. save script

        delete [] tmp;

        script+=9; // bypass </script>

        return script;

    };

    void parse_go(html_document* document, char* html_text)
    {
        char* current = html_text;

        html_doc = document; // save

        while(*current)
        {
            if (*current == '<') {

                ++current;

                switch (*current) {

                case '!':
                    ++current;
                    current = (*current == '-') ? parse_comment(current) : parse_dtd(current);
                    break;

                case '/':
                    current = close_tag(current);
                    break;

                default:
                    current = open_tag(current);
                    break;
                }

            }else{
                current = save_text(html_doc->current_t,current);
            }
        }

        // add a NULL to the end of the tags (for end checking in loops)
        document->html_t.push_back(0);
    };

    char* save_text(html_tag* tag,char* text)
    {
        char* position = text;

        while(*position && *position != '<')
            ++position;

        char* make_text = new char[(position-text)+1];
        char* ptr = make_text;

        int  i = 0;
        int  total_len = position-text;
        html_tag* c = html_doc->current_t;

        for(i=0;i<(position-text);i++)
        {
            // -- only keep \n \t if inside a pre tag
            if (c)
            {
                if ((text[i] == '\n' || text[i] == '\t') && (c->tag_hash != hash_pre))
                {
                    total_len--;
                    continue;
                }
            }

            *ptr++ = text[i];
        }
        *ptr = 0;

        if (total_len <= 0) {
            delete [] make_text;
            return position;
        }

        html_tag* x = new html_tag();
        x->name = "_text";  // remove later
        x->tag_hash = create_hash((char*)"_text");
        x->parent_tag = c;

        html_doc->html_t.push_back(x);

        x->text = make_text;

        delete [] make_text;

        return position;

    };

    char* parse_dtd(char* html)
    {
        //ignore for now !!!
        while (*html && *html != '>')
            ++html;

        html++; //bypass '>'

        return html;
	};

    char* parse_comment(char *html)
    {
        char *tmp;
        const char *tmp2;

        while (*html == '-' || isspace(*html))
            ++html;

        tmp2 = html;
        while (*html && !(*html == '-' && *(html + 1) == '-' && *(html + 2) == '>'))
            ++html;

        tmp = new char[(html - tmp2 + 1)];
        if (!tmp)
            return 0;

        strncpy(tmp, tmp2, html - tmp2);
        tmp[html - tmp2] = 0;

        if (*(html + 3))
            html += 3;

        html_tag* x = new html_tag();
        x->name = "_comment"; // remove later
        x->tag_hash = create_hash((char*)"_comment");
        x->text = tmp;

        html_doc->html_t.push_back(x);

        delete [] tmp;

        return html;
    };


};


#endif // HTML_PARSER_H_INCLUDED
